package com.eagle.coders.common.ui.form.renderer;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

import org.dom4j.Document;
import org.dom4j.DocumentFactory;
import org.dom4j.Element;

import com.eagle.coders.common.bean.annotations.DomainTitle;
import com.eagle.coders.common.form.annotations.DomainObject;
import com.eagle.coders.common.form.annotations.type.DomainPerpertiesAllowType;
import com.eagle.coders.common.ui.form.renderer.cache.UIDomainExceptPropertyBean;
import com.eagle.coders.common.ui.form.renderer.cache.UIDomainPropertyBean;
import com.eagle.coders.common.ui.form.renderer.util.UiColumnComparator;
import com.eagle.coders.common.ui.interfaces.IComponent;

/**
 * @author Anees
 * 
 */
public class BeanRenderer {

//	private static Document document;
//
//	private static Element domainElement;
//
//	static {
//		document = DocumentFactory.getInstance().createDocument();
//		domainElement = document.addElement("domain");
//	}

	/**
	 * 
	 * @param bean
	 */
	public static String beanRenderer(DomainObject domainObject,
			List<UIDomainExceptPropertyBean> uiDomainPropertyExepHolderList, Element domainElement) {

		Class<?> bean = domainObject.domainObject();
		List<UIDomainPropertyBean> uiDomainPropertyList = getAllowedPropertiesOfDomainObjects(
				bean,
				uiDomainPropertyExepHolderList,
				processMethods(bean.getDeclaredMethods(), bean.getDeclaredFields()));
		
		SortedMap<Integer, List<UIDomainPropertyBean>> rowPropertyHolderMap = 
			sortUiDomainProperties(uiDomainPropertyList);
				
		domainElement.addAttribute("name", bean.getName());

		if(bean.isAnnotationPresent(DomainTitle.class)){
			DomainTitle domainTitle = bean.getAnnotation(DomainTitle.class);
			domainElement.addAttribute("title", domainTitle.title());
		}
		UiFormDecorator formDecorator = new UiFormDecorator();
		
//		UiFormDecorator.getInstance().decorator(domainElement, rowPropertyHolderMap);
		formDecorator.decorator(domainElement, rowPropertyHolderMap);
//		System.out.println("[BeanRendere] {beanRenderer} :: xml :: \n"+domainElement.asXML());
		return domainElement.asXML();
	}
	

	/**
	 * 
	 * @param propertyBeans
	 * @return
	 */
	private static SortedMap<Integer, List<UIDomainPropertyBean>> sortUiDomainProperties(
			final List<UIDomainPropertyBean> propertyBeans) {

		SortedMap<Integer, List<UIDomainPropertyBean>> rowPropertyHolderMap = new TreeMap<Integer, List<UIDomainPropertyBean>>();
		for (UIDomainPropertyBean uiDomainPropertyBean : propertyBeans) {
			if (null != uiDomainPropertyBean.getUiPositionRow()) {
				if (rowPropertyHolderMap.containsKey(uiDomainPropertyBean.getUiPositionRow())) {
					List<UIDomainPropertyBean> propertyHolderList1 = 
						rowPropertyHolderMap.get(uiDomainPropertyBean.getUiPositionRow());
					propertyHolderList1.add(uiDomainPropertyBean);
					Collections.sort(propertyHolderList1, new UiColumnComparator());
					rowPropertyHolderMap.put(uiDomainPropertyBean.getUiPositionRow(), propertyHolderList1);
				} else {
					List<UIDomainPropertyBean> propertyHolderList0 = new ArrayList<UIDomainPropertyBean>();
					propertyHolderList0.add(uiDomainPropertyBean);
					rowPropertyHolderMap.put(uiDomainPropertyBean.getUiPositionRow(), propertyHolderList0);
				}
			}
		}
		return rowPropertyHolderMap;
	}

	/**
	 * 
	 * @param methods
	 * @param fields
	 */
	public static List<UIDomainPropertyBean> processMethods(Method methods[],
			Field fields[]) {
		List<UIDomainPropertyBean> holder = new ArrayList<UIDomainPropertyBean>();
		for (Method method : methods) {
			String name = method.getName();
			Class<?> returnType = method.getReturnType();
			if (name.startsWith("get")) {
				String propertyName = getPropertyFromMethod(name);
				boolean flag = compareFieldAndMethod(propertyName, fields);
				if (flag) {
					UIDomainPropertyBean propertyHolder = BeansAnnotationProcessor
							.processAnnotation(propertyName, method);
					propertyHolder.setPropertyReturnType(returnType);
					holder.add(propertyHolder);
				}
			}
		}
		return holder;
	}
	
	/**
	 * 
	 * @param c
	 * @return
	 */
	public static void processBeansProperty(Class<? extends Object> c, String propertyName, UIDomainPropertyBean domainPropertyBean){
		try {
			String originalName = propertyName.replaceFirst(propertyName.substring(0,
					1), propertyName.substring(0, 1).toUpperCase());
			String methodName = "get"+originalName;
			Method method= c.getMethod(methodName, null);
			BeansAnnotationProcessor.processByReturnType(method, domainPropertyBean, propertyName);
		} catch(Exception e){
			e.printStackTrace();
		}
	}
	
	/**
	 * 
	 * @param c
	 * @return
	 */
	public static List<UIDomainPropertyBean> getBeanIntrospection(Class<? extends Object> c) {
		try {
			Method[] methods = c.getDeclaredMethods();
			Field[] fields = c.getDeclaredFields();
			return processMethods(methods, fields);
		} catch(Exception e){
			e.printStackTrace();
			return new ArrayList<UIDomainPropertyBean>();
		}
	}

	/**
	 * 
	 * @param name
	 * @return
	 */
	public static String getPropertyFromMethod(String name) {

		String orginalName = name.substring(3, name.length());

		String propertyName = orginalName.replaceFirst(orginalName.substring(0,
				1), orginalName.substring(0, 1).toLowerCase());

		return propertyName;
	}
	

	/**
	 * 
	 * @param fieldName
	 * @param fields
	 * @return
	 */
	private static boolean compareFieldAndMethod(String fieldName,
			Field fields[]) {
		boolean flag = false;
		for (Field field : fields) {
			if (fieldName.equals(field.getName())) {
				flag = true;
			}
		}
		return flag;
	}

	private static List<UIDomainPropertyBean> getAllowedPropertiesOfDomainObjects(
			Class<?> bean,
			List<UIDomainExceptPropertyBean> uiDomainPropertyExepHolderList,
			List<UIDomainPropertyBean> uiDomainPropertyList) {
		List<UIDomainPropertyBean> uiDomainPropertyList0 = null;
		if (null != uiDomainPropertyExepHolderList && uiDomainPropertyExepHolderList.size() > 0) {
			for (UIDomainExceptPropertyBean uiDomainExcepPeroperty : uiDomainPropertyExepHolderList) {
				if (bean.equals( uiDomainExcepPeroperty.getDomainObject())) {
					uiDomainPropertyList0 = validatePropertiesViaPolicy(
							uiDomainExcepPeroperty.getPolicy(),
							uiDomainPropertyList,
							uiDomainExcepPeroperty.getPropertyList());
					
				}
			}
			if(null != uiDomainPropertyList0)
				return uiDomainPropertyList0;
			else
				return uiDomainPropertyList;
//			return uiDomainPropertyList;
		} else
			return uiDomainPropertyList;
	}

	/**
	 * TODO: i need to verify this method in future
	 * 
	 * @param policy
	 * @param uidomainPeroertyHolderList
	 * @param properties
	 */
	private static List<UIDomainPropertyBean> validatePropertiesViaPolicy(
			DomainPerpertiesAllowType policy,
			List<UIDomainPropertyBean> uidomainPeroertyHolderList,
			List<String> properties) {

		List<UIDomainPropertyBean> updatedPropertyHolderList = new ArrayList<UIDomainPropertyBean>();
		if (policy.equals(DomainPerpertiesAllowType.ALLOW_ALL)) {
				for (UIDomainPropertyBean uiDomainPropertyHolder : uidomainPeroertyHolderList) {
					if(!properties.contains(uiDomainPropertyHolder.getDomainProperty())){
						updatedPropertyHolderList.add(uiDomainPropertyHolder);
					}
				}
		} else if(policy.equals(DomainPerpertiesAllowType.DENY_ALL)) {
			for (String property : properties) {
				for (UIDomainPropertyBean uiDomainPropertyHolder : uidomainPeroertyHolderList) {
					if (property.equals(uiDomainPropertyHolder
							.getDomainProperty())) {
						updatedPropertyHolderList.add(uiDomainPropertyHolder);
					}
				}
			}
		}
		return updatedPropertyHolderList;
	}
}